const fs = require('fs')
const path = require('path')
const URL = require('url')
const querystring = require('querystring');
const request = require('request-promise-native');
const cheerio = require('cheerio')
const crypto = require('crypto');
const {
	JSDOM
} = require("jsdom");
const {
	getMSTime,
} = require('./dateUtils')

const $log_info = (defaults = '[DEFAULTS]', ...args) => {
	args = args.map(val => {
		if (val && /^\[[\s\S]*\]$/.test(val))
			return val
		if (typeof val == 'object')
			return `[${JSON.stringify(val)}]`
		return `[${val}]`
	})
	if (Array.isArray(defaults)) {
		defaults = defaults.map(val => {
			if (val && /^\[[\s\S]*\]$/.test(val))
				return val
			if (typeof val == 'object')
				return `[${JSON.stringify(val)}]`
			return `[${val}]`
		})
	}
	console.log(`[${getMSTime()}]`, ...defaults, ...args)
}


const $log_err = (source, ...args) =>
	console.error(`[${getMSTime()}]`, `[${source}]`, ...args)

const httpClient = function httpClient(options) {
	if (typeof options == 'string') {
		let url = options
		options = {
			url,
		}
	}
	options.method = options.method || 'GET'
	return request(options)
}

const _defaultDOM = new JSDOM('<!DOCTYPE html><html><body><input id="_default_dom_0000_" name="_xxx_yyy_zzz_" value="_DEFAULT_DOM_" /></body></html>')

const Utils = {

	request,

	getMSTime,

	$md5(val) {
		return crypto.createHash('md5').update(val).digest('hex');
	},

	getMaxResponse: async function(asyncReq, options, params = {}) {
		let max = params.max || 5
		let count = 1
		let ms = params.ms
		await Utils.sleepRandom(ms, count)
		let log = [
			process.pid,
			'asyncReq',
			asyncReq.name,
			JSON.stringify(options)
		]
		let result = await asyncReq(options)
		while (!result && options) {
			count += 1
			await Utils.sleepRandom(ms, count)
			$log_info([
				process.pid,
				'asyncReq_count',
				count, max, asyncReq.name,
				JSON.stringify(options)
			])
			result = await asyncReq(options, count > max)
		}
		return result
	},

	$log_info(defaults) {
		return (...args) => $log_info(defaults, ...args)
	},

	$log_err(source = '[DEFAULT]') {
		return (...args) => $log_err(source, ...args)
	},

	sleepRandom(time, count) {
		count = count < 1 ? 1 : count
		time = time < 1 ? parseInt(Math.random() * 1000) : time
		time *= count
		return new Promise((resolve, reject) => {
			$log_info(['---时间间隔---', `${time} ms`])
			setTimeout(() => resolve(true), time)
		});
	},

	getJSON(options, next) {
		return new Promise((resolve, reject) => {
			httpClient(options).then(result => {
				try {
					result = JSON.parse(result)
				} catch (e) {
					$log_err('---JSON.parse---', e)
				}
				resolve(result)
			}).catch(err => {
				let {
					statusCode,
					message,
					error,
				} = err
				$log_err(`---JSON ${JSON.stringify(options)} ---`, `[${statusCode}]`, `[${message}]`)
				let result = next === true ? {} : false
				resolve(result)
			})
		});
	},

	getResponse(options, next) {
		return new Promise((resolve, reject) => {
			httpClient(options).then(result => {
				resolve(result)
			}).catch(err => {
				let {
					statusCode,
					message,
					error,
				} = err
				$log_err(`---Text ${JSON.stringify(options)} ---`, `[${statusCode}]`, `[${message}]`)
				let result = next === true ? '{}' : false
				resolve(result)
			})
		});
	},

	getDOM(options, next) {
		return new Promise((resolve, reject) => {
			httpClient(options).then(res => {
				resolve(new JSDOM(res))
			}).catch(err => {
				$log_err(`---Window [${JSON.stringify(options)}] ---`, `[${err}]`)
				let result = next === true ? _defaultDOM : false
				resolve(result)
			})
		});
	},

	getWindow(options, next) {
		return new Promise((resolve, reject) => {
			httpClient(options).then(res => {
				let dom = new JSDOM(res)
				resolve(dom.window)
			}).catch(err => {
				$log_err(`---Window [${JSON.stringify(options)}] ---`, `[${err}]`)
				let result = next === true ? _defaultDOM.window : false
				resolve(result)
			})
		});
	},

	getDocument(options, next) {
		return new Promise((resolve, reject) => {
			httpClient(options).then(res => {
				let dom = new JSDOM(res)
				resolve(dom.window.document)
			}).catch(err => {
				$log_err(`---Document [${JSON.stringify(options)}] ---`, err)
				let result = next === true ? _defaultDOM.window.document : false
				resolve(result)
			})
		});
	},

	get$: function(options) {
		return new Promise((resolve, reject) => {
			httpClient(options).then(html => {
				resolve(cheerio.load(html))
			}).catch(err => {
				$log_err(`---$ ${options.url} ---`, err)
				resolve(false)
			})
		});
	},

	getUrlParams(url) {
		let search = URL.parse(url).search
		return querystring.parse(search && search.replace(/^\?/, '') || '')
	},

	/**
	 * [isDocumentFailed description]
	 * @param  {[type]}  document [description]
	 * @param  {[type]}  params   [description]
	 * @param  {Boolean} stop     [description]
	 * @return {Boolean}          [description]
	 */
	isDocumentFailed(document, params, stop = false) {
		let input = document.getElementById('_default_dom_0000_')
		if (input && input.value == '_DEFAULT_DOM_' && input.name == '_xxx_yyy_zzz_') {
			if (stop) {
				process.send({
					kill: 1,
					stop: true,
				})
				if (this.__task && typeof this.__task.stop == 'function')
					this.__task.stop({
						message: params,
					}).then(r => {
						process.exit(1)
					})
			}
			return true
		}
		return false
	},

	/**
	 * [mkdirsSync description]
	 * @param  {[type]} dirPath [description]
	 * @return {[type]}         [description]
	 */
	mkdirsSync(dirPath) {
		if (!fs.existsSync(dirPath)) {
			let pathTmp = ''
			let dirPathArr = dirPath.split(path.sep)
			for (let i = 0; i < dirPathArr.length; i++) {
				let dirname = dirPathArr[i]
				if (!dirname)
					dirname = path.sep;
				if (pathTmp) {
					pathTmp = path.join(pathTmp, dirname);
				} else {
					pathTmp = dirname;
				}
				if (!fs.existsSync(pathTmp)) {
					fs.mkdirSync(pathTmp)
				}
			}
		}
		return true;
	},

}

module.exports = Utils